<template>
  <div ref="containerRef" id="model-view"></div>
</template>

<script setup>
import {
  ref,
  onMounted,
  nextTick,
  onUnmounted,
  onBeforeUnmount,
  getCurrentInstance,
} from "vue";
import * as ElementPlusIconsVue from "@element-plus/icons-vue";
import * as THREE from "three";
import Viewer from "@/common/threeModules/Viewer";
import Lights from "@/common/threeModules/Lights";
import Labels from "@/common/threeModules/Labels";
import ModelLoader from "@/common/threeModules/ModelLoader.js";
import Transform from "@/common/threeModules/Transform.js";
import Pipes from "@/common/threeModules/Pipes.js";
import {
  useMeshEditStore,
  useLabelListStore,
  formDataStore,
} from "@/stores/modules/model.js";
import { GLTFExporter } from "three/addons/exporters/GLTFExporter.js";
import { initModelData } from "@/components/model/initData.js";
const { $bus } = getCurrentInstance().proxy;
const meshStore = useMeshEditStore();
const labelStore = useLabelListStore();
const formStore = formDataStore();

const props = defineProps({
  factoryId: {
    type: String,
    default: "",
  },
});
const containerRef = ref(null);
let viewer = null;
let modelLoader = null;
let pipeLoader = null;
let transform = null;
let labelIns = null; // 标签实例
let commonModels = [];
const commonRecord = [];
const exporter = new GLTFExporter();

onMounted(() => {
  nextTick(() => {
    initScene();
    initData();
    $bus.on("add-model", (modelInfo) => handleAddModel(modelInfo));
    $bus.on("add-pipe", () => handleAddPipe());
    $bus.on("cancel-pipe", () => handleCancelPipe());
    $bus.on("model-upload", (param) => handleUpload(param, true));
    $bus.on("add-label", (modelInfo) => {
      const position = viewer.calMousePosition(
        modelInfo.clientX,
        modelInfo.clientY
      );
      const iconName = modelInfo.modelApi.iconName;
      const len = labelStore.labelList.length + 1;
      handleAddLabel(position, iconName, `标签-${len}`);
    });
    $bus.on("download-cover", () => handleDownloadCover());
    $bus.on("download-model", (type) => handleExportModleFile(type));
  });
});

onBeforeUnmount(() => {
  viewer.handleBeforeUnmount();
});

const initScene = () => {
  viewer = new Viewer(containerRef.value);
  viewer.camera.position.set(107, 45, 200);
  viewer.orbitControl.maxPolarAngle = Math.PI / 2.1; // 限制orbitControl的上下角度范围

  viewer.renderer.shadowMap.enabled = true;
  viewer.renderer.shadowMap.type = THREE.PCFSoftShadowMap;

  const lights = new Lights(viewer);
  const ambientLight = lights.addAmbientLight(); // 添加环境光
  ambientLight.setOption({
    color: 0xffffff,
    intensity: 1, // 环境光强度
  });
  ambientLight.light.name = "AmbientLight";
  // 添加平行光
  lights.addDirectionalLight([100, 100, 100], {
    color: "rgb(253,253,253)",
    intensity: 4.4,
    castShadow: true, // 是否投射阴影
  });
  lights.addDirectionalLight([-100, -100, 100], {
    color: "rgb(253,253,253)",
    intensity: 4.4,
    castShadow: true, // 是否投射阴影
  });
  lights.addDirectionalLight([-100, 100, -100], {
    color: "rgb(253,253,253)",
    intensity: 4.4,
    castShadow: true, // 是否投射阴影
  });
  lights.addDirectionalLight([100, -100, -100], {
    color: "rgb(253,253,253)",
    intensity: 4.4,
    castShadow: true, // 是否投射阴影
  });

  modelLoader = new ModelLoader(viewer);
  pipeLoader = new Pipes(viewer);
  transform = new Transform(viewer);
  viewer.addAxis();
  labelIns = new Labels(viewer);
  meshStore.setLabelIns(labelIns);
  // 添加状态检测
  viewer.addStats();
};

const initData = () => {
  initModelData(
    props.factoryId,
    (param) => {
      loadCommonModel(param.commonTypeId, param);
    },
    (points) => {
      pipeLoader.loadPipe(points);
    },
    (tag) => {
      const { positionX, positionY, positionZ, iconName } = tag;
      const uuid = handleAddLabel(
        [positionX, positionY, positionZ],
        iconName,
        `标签`
      ).uuid;
      tag.uuid = uuid;
      labelIns.updateLabelElement(tag);
    },
    (param, fileId, filename) => {
      handleUpload(param).then((uuid) => {
        meshStore.addUploadModel({
          uuid,
          filename,
          fileId,
        });
      });
    }
  );
};

// 上传
const handleSubmit = async () => {
  return new Promise(async (reslove) => {
    let commonModelFrom = [];
    let selfModelFrom = [];
    let pipeFrom = [];
    let labelFrom = [];

    // 模型
    for (let i = 0; i < viewer.objects.length; i++) {
      let commonTypeId = "";
      commonRecord.some((item) => {
        if (item.uuid === viewer.objects[i].uuid) {
          commonTypeId = item.commonTypeId;
          return true;
        }
      });
      const { position, rotation, scale } = viewer.objects[i];
      const transformParam = {
        positionX: position.x,
        positionY: position.y,
        positionZ: position.z,
        rotationX: rotation.x,
        rotationY: rotation.y,
        rotationZ: rotation.z,
        scaleX: scale.x,
        scaleY: scale.y,
        scaleZ: scale.z,
      };
      if (commonTypeId !== "") {
        const model = { commonTypeId, transformParam };
        commonModelFrom.push(model);
      } else {
        meshStore.uploadModels.some((item) => {
          if (item.uuid === viewer.objects[i].uuid) {
            selfModelFrom.push({
              filename: item.filename,
              fileId: item.fileId,
              transformParam,
            });
            return true;
          }
        });
      }
    }
    // 管道
    for (let i = 0; i < viewer.pipes.length; i++) {
      let points = [];
      pipeLoader.pipeRecord.some((item) => {
        if (item.uuid === viewer.pipes[i].uuid) {
          points = item.points;
          return true;
        }
      });
      const model = { points };
      pipeFrom.push(model);
    }
    // 标签
    for (let i = 0; i < labelIns.labelList.length; i++) {
      const model = labelIns.labelList[i];
      labelFrom.push(model);
    }

    formStore.setCommonModels(commonModelFrom);
    formStore.setSelfModels(selfModelFrom);
    formStore.setPipes(pipeFrom);
    formStore.setLabels(labelFrom);
    reslove();
  });
};

// 下载封面
const handleDownloadCover = () => {
  var link = document.createElement("a");
  var canvas = viewer.renderer.domElement;
  link.href = canvas.toDataURL("image/png");
  link.download = `${new Date().toLocaleString()}.png`;
  link.click();
  ElMessage.success("下载成功");
};

// 下载模型
const handleExportModleFile = (type) => {
  const options = {
    trs: true, // 是否保留位置、旋转、缩放信息
    binary: type == "glb" ? true : false, // 是否以二进制格式输出
    embedImages: true, //是否嵌入贴图
    onlyVisible: true, //是否只导出可见物体
    includeCustomExtensions: true,
  };
  exporter.parse(
    viewer.scene,
    function (result) {
      if (result instanceof ArrayBuffer) {
        // 将结果保存为GLB二进制文件
        saveArrayBuffer(result, `${new Date().toLocaleString()}.glb`);
      } else {
        // 将结果保存为GLTF JSON文件
        saveString(
          JSON.stringify(result),
          `${new Date().toLocaleString()}.gltf`
        );
      }
      function saveArrayBuffer(buffer, filename) {
        // 将二进制数据保存为文件
        const blob = new Blob([buffer], { type: "application/octet-stream" });
        const url = URL.createObjectURL(blob);
        const link = document.createElement("a");
        link.href = url;
        link.download = filename;
        link.click();
        URL.revokeObjectURL(url);
        ElMessage.success("导出成功");
      }
      function saveString(text, filename) {
        // 将字符串数据保存为文件
        const blob = new Blob([text], { type: "application/json" });
        const url = URL.createObjectURL(blob);
        const link = document.createElement("a");
        link.href = url;
        link.download = filename;
        link.click();
        URL.revokeObjectURL(url);
        ElMessage.success("导出成功");
      }
    },
    (err) => {
      ElMessage.error(err);
    },
    options
  );
};

/**
 * 添加模型：获取模型信息
 */
const handleAddModel = (modelInfo) => {
  const {
    clientX,
    clientY,
    modelApi: { id: commonTypeId, path },
  } = modelInfo;
  const position = viewer.calMousePosition(clientX, clientY);

  loadCommonModel(commonTypeId, { path, position });
};

/**
 * 加载系统模型
 */
const loadCommonModel = (commonTypeId, param) => {
  let commonModel = findCommonModel(commonTypeId);

  if (commonModel) {
    let model = commonModel.cloneModel(param.position);
    const { uuid } = model.object;
    commonRecord.push({ uuid, commonTypeId });
    viewer.objects.push(model.object);
  } else {
    modelLoader.loadModelToScene(param, (model) => {
      model.openCastShadow();
      commonModels.push({
        commonTypeId,
        model,
      });
      const { uuid } = model.object;
      commonRecord.push({ uuid, commonTypeId });
    });
  }
};

/**
 * 添加管道
 */
const handleAddPipe = () => {
  pipeLoader.addPipe();
};

/**
 * 取消添加管道
 */
const handleCancelPipe = () => {
  pipeLoader.cancelPipe();
};

/**
 * 添加标签
 */
const handleAddLabel = (position, iconName, input) => {
  const label = labelIns.addCss3dLabel(position, iconName, input);
  labelStore.setLabelList(labelIns.labelList);
  return label;
};

/**
 * 判断是否克隆
 */
const findCommonModel = (id) => {
  let model;
  commonModels.some((commonModel) => {
    if (commonModel.commonTypeId === id) {
      model = commonModel.model;
      return true;
    }
  });
  return model;
};

/**
 * 解析模型->blob
 */
const parseModel = (object) => {
  return new Promise(async (reslove, reject) => {
    // 用户本地模型
    const options = {
      trs: true, // 是否保留位置、旋转、缩放信息
      binary: true, // 是否以二进制格式输出
      embedImages: true, //是否嵌入贴图
      onlyVisible: true, //是否只导出可见物体
      includeCustomExtensions: true,
    };
    exporter.parse(
      object,
      function (result) {
        if (result instanceof ArrayBuffer) {
          // 将结果保存为GLB二进制文件
          saveArrayBuffer(result, `${new Date().toLocaleString()}.glb`);
          function saveArrayBuffer(buffer, filename) {
            // 将二进制数据保存为文件
            const blob = new Blob([buffer], {
              type: "application/octet-stream",
            });
            reslove(blob);
          }
        }
      },
      (err) => {
        ElMessage.error(err);
        reject(err);
      },
      options
    );
  });
};

/**
 * 加载外部模型
 */
const handleUpload = async (param, upload) => {
  let uuid = "";
  try {
    await modelLoader.loadModelToScene(param, (model) => {
      model.openCastShadow();
      uuid = model.object.uuid;
      if (upload) {
        // 解析文件
        parseModel(model.object).then((blob) => {
          // 上传到服务器
          $bus.emit("upload-success", { uuid: model.object.uuid, blob });
        });
      }
    });
    // TODO: 加载成功之后手动释放 否则会造成内存浪费
    URL.revokeObjectURL(param.path);
    ElMessage.success("加载成功");
    return uuid;
  } catch (err) {
    ElMessage.error("加载失败");
  }
};

defineExpose({
  handleSubmit,
});
</script>

<style lang="scss" scoped>
#model-view {
  width: calc(100% - 305px);
  height: calc(100vh - 35px) !important;
  position: relative;
}
</style>